package com.kvest.map.mapview.engine.model;

import android.util.Log;

import javax.microedition.khronos.opengles.GL10;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * User: r.belokon
 * Date: 22.04.13
 * Time: 12:11
 * To change this template use File | Settings | File Templates.
 */
public class OSMModel extends AbstractModel {
    private static final float DELTA_ZOOM = 0.1f;
    private static final int DEFAULT_ZOOM = 2;
    private static final int MIN_ZOOM = 1;
    private static final int MAX_ZOOM = 18;

    private MapBundle mapBundle;
    private List<Tile> tiles;
    private int zoom;

    public OSMModel() {
        mapBundle = new MapBundle();
        tiles = new ArrayList<Tile>(40);
        zoom = DEFAULT_ZOOM;
    }

    @Override
    public void draw(GL10 gl) {
        setCamera(gl);

        drawTiles(gl);
    }

    private void setCamera(GL10 gl) {
        gl.glTranslatef(-x, y, z);
    }

    private void drawTiles(GL10 gl) {
        //Обновляем тайлы
        TileTextureManager.getInstance().update(gl);

        //Рисуем тайлы
        synchronized (tiles) {
            for (Tile tile : tiles) {
                tile.draw(gl);
            }
        }
    }

    @Override
    public void onSurfaceChanged() {
        //ToDo Подумать когда может возникнуть и что делать
    }

    @Override
    public void invalidate() {
        update();
    }

    @Override
    public void zoomIn() {
        z -= DELTA_ZOOM;

        //ToDo
        //if (!!!)
    }

    @Override
    public void zoomOut() {
        z += DELTA_ZOOM;

        //ToDo
      //  if (!!!)
    }

    private void update() {
        //Определяем видимые тайлы
        mapBundle.recalc(x, y, z, zoom);
        //ToDo Delete
//        Log.d("KVEST_TAG", "x=" + x + ", y=" + y);
//        Log.d("KVEST_TAG", "x=[" + mapBundle.getXBegin() + ", " + mapBundle.getXEnd() + "]; y=[" +
//                                + mapBundle.getYBegin() + ", " + mapBundle.getYEnd() + "]");

        boolean[][] tilesExists = new boolean[mapBundle.getYEnd() - mapBundle.getYBegin() + 1][mapBundle.getXEnd() - mapBundle.getXBegin() + 1];
        for (int i = 0; i < tilesExists.length; ++i) {
            Arrays.fill(tilesExists[i], false);
        }

        //Удалить лишние тайлы
        synchronized (tiles) {
            Tile tile;
            for (int i = tiles.size() - 1; i >= 0 ; --i) {
                tile = tiles.get(i);
                if (tile.getZoom() != zoom) {
                    //Удалить по зуму
                    tiles.remove(i);
                    tile.recycle();
                } else if (tile.getX() < mapBundle.getXBegin() || tile.getX() > mapBundle.getXEnd() ||
                           tile.getY() < mapBundle.getYBegin() || tile.getY() > mapBundle.getYEnd()) {
                    //Удалить за пределами
                    tiles.remove(i);
                    tile.recycle();
                } else {
                    tilesExists[tile.getY() - mapBundle.getYBegin()][tile.getX() - mapBundle.getXBegin()] = true;
                }
            }
        }

        //Добавить нехватающие
        for (int y = 0; y < tilesExists.length; ++y) {
            for (int x = 0; x < tilesExists[y].length; ++x) {
                if (!tilesExists[y][x]) {
                    synchronized (tiles) {
                        tiles.add(new Tile(mapBundle.getXBegin() + x, mapBundle.getYBegin() + y, zoom));
                    }
                }
            }
        }
    }

    @Override
    public void onSurfaceCreated() {
        //ToDo delete this
        zoom = 4;
        update();
    }

    private static class MapBundle {
        private int xBegin;
        private int xEnd;
        private int yBegin;
        private int yEnd;

        public MapBundle() {
            xBegin = 0;
            xEnd = 0;
            yBegin = 0;
            yEnd = 0;
        }

        public void recalc(float x, float y, float z, int zoom) {
            //ToDo Потом переделать с учетом наклонов камеры, поворотов(лево-право) и т.д.
            xBegin = Math.max(0, Math.round(x) - 2);
            xEnd = Math.min(max(zoom) , xBegin + 4);
            yBegin = Math.max(0, Math.round(y) - 3);
            yEnd = Math.min(max(zoom) , yBegin + 6);
        }

        public int getXBegin() {
            return xBegin;
        }

        public int getXEnd() {
            return xEnd;
        }

        public int getYBegin() {
            return yBegin;
        }

        public int getYEnd() {
            return yEnd;
        }

        public static int max(int zoom) {
            return (int)Math.pow(2.0, zoom) - 1;
        }
    }
}
